﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Microsoft.WindowsAzure.StorageClient;
using System.Threading.Tasks;
using System.Threading;

namespace AzureDriveCore
{
	public class BlobRequestContext : RequestContextBase
	{
		public BlobRequestContext(AzureBlobController controller, string name)
			: base(controller)
		{
			this.Name = name;

			this.CreatedDate = DateTime.Now;

			this._controller = controller;
		}

		private CloudBlob _blobReference;
		private AzureBlobController _controller;
		private int _lockCount;
		private object _readWriteLock = new object();
		private Stream _readStream;
		private Stream _writeStream;

		public int ID { get; set; }

		public string Name { get; set; }

		public void IncrementLock()
		{
			Interlocked.Increment(ref _lockCount);
		}

		public override bool CreateFile()
		{
			lock (_readWriteLock)
			{
				this.SetupBlobReference();

				if (this.FileExists)
				{
					return true;
				}
				else
				{
					var container = _controller.CloudBlobClient.GetLowestContainer(this.Name);

					try
					{
						if (container != null)
						{
							string blobName = this.Name.Substring(container.Name.Length + 1);

							this._blobReference = container.GetBlobReference(blobName);

							this._blobReference.UploadByteArray(new byte[0]);
						}
						else
						{
							this._blobReference = _controller.CloudBlobClient.GetBlobReference(this.Name);

							this._blobReference.UploadByteArray(new byte[0]);
						}
					}
					catch (Exception ex)
					{
						Console.WriteLine("ERROR: {0}", ex);

						return false;
					}

					return true;
				}
			}
		}

		public override void Dispose()
		{
			lock (_controller.RequestStore)
			{
				if (--this._lockCount == 0)
				{
					_controller.RequestStore.Remove(this.Name);
				}
				else
				{
					return;
				}
			}

			lock (this._readWriteLock)
			{
				if (this._blobReference != null)
				{
					this._blobReference = null;
				}

				if (this._readStream != null)
				{
					try { this._readStream.Dispose(); }
					catch { }

					this._readStream = null;
				}

				if (this._writeStream != null)
				{
					try { this._writeStream.Dispose(); }
					catch { }

					this._writeStream = null;
				}
			}
		}

		public override bool DeleteDirectory()
		{
			lock (_readWriteLock)
			{
				bool success = false;

				try
				{
					var containerReference = this._controller.CloudBlobClient.GetContainerReference(this.Name);

					// Try to delete the container.
					try
					{
						containerReference.Delete();
						containerReference = null;
						success = true;
					}
					catch { }

					containerReference = null;

					// Delete any directories if they exist.
					var directoryReference = this._controller.CloudBlobClient.GetBlobDirectoryReference(this.Name);

					Parallel.ForEach(directoryReference.ListBlobs(new BlobRequestOptions { UseFlatBlobListing = true, }).OfType<CloudBlob>(), item =>
					{
						try
						{
							item.DeleteIfExists();
							success = true;
						}
						catch { }
					});
				}
				catch { }

				return success;
			}
		}

		public override bool DeleteFile()
		{
			lock (_readWriteLock)
			{
				if (this._readStream != null)
				{
					try { this._readStream.Dispose(); }
					catch { }

					this._readStream = null;
				}

				try
				{
					if (this._blobReference == null)
					{
						this._blobReference = _controller.CloudBlobClient.GetBlobReference(this.Name);
					}

					this._blobReference.DeleteIfExists();

					return true;
				}
				catch { return false; }
			}
		}

		public override bool InitializeAsFile()
		{
			if (this.FileExists)
			{
				return true;
			}

			lock (_readWriteLock)
			{
				this.SetupBlobReference();

				return (this._blobReference != null);
			}
		}

		public override uint ReadData(byte[] buffer, long offset)
		{
			lock (_readWriteLock)
			{
				if (this._readStream == null)
				{
					if (this._blobReference == null)
					{
						this.SetupBlobReference();
					}

					this._readStream = this._blobReference.OpenRead();
				}

				this._readStream.Seek(offset, SeekOrigin.Begin);

				return (uint)this._readStream.Read(buffer, 0, buffer.Length);
			}
		}

		private void SetupBlobReference()
		{
			var blobReference = _controller.CloudBlobClient.GetBlobReference(this.Name);

			if (blobReference.Exists())
			{
				this._blobReference = blobReference;

				this.FileExists = true;
				this.FileSize = blobReference.Properties.Length;
				this.CreatedDate = blobReference.Properties.LastModifiedUtc;
			}
		}

		public override uint WriteData(byte[] buffer, long offset)
		{
			lock (_readWriteLock)
			{
				if (this._writeStream == null)
				{
					if (this._blobReference == null)
					{
						this.FileExists = true;
						this.CreatedDate = DateTime.UtcNow;
						this._blobReference = _controller.CloudBlobClient.GetBlobReference(this.Name);
					}

					this._writeStream = this._blobReference.OpenWrite();
				}

				//this._writeStream.Seek(offset, SeekOrigin.Begin);

				this._writeStream.Write(buffer, 0, buffer.Length);

				return (uint)buffer.Length;
			}
		}
	}
}
